<template>
	<div class="ER">
		<div id="container" style="width: 100%; height: 800px"></div>
	</div>
</template>
<script>
import G6 from '@antv/g6';
const { Util, registerBehavior, registerEdge, registerNode } = G6;
function throttle(fun, delay) {
	let flag = true;
	return function () {
		if (!flag) {
			return;
		}
		flag = false;
		setTimeout(() => {
			fun.apply(this, arguments);
			flag = true;
		}, delay);
	};
}

import rawData from './data.js';

const isInBBox = (point, bbox) => {
	const { x, y } = point;
	const { minX, minY, maxX, maxY } = bbox;

	return x < maxX && x > minX && y > minY && y < maxY;
};

const itemHeight = 30;
export default {
	name: 'ER',
	mounted() {
		this.initG6();
	},
	methods: {
		previousFieldHighlight(graph, form, to) {
			// window.console.log('graph,nodeId,key', graph, nodeId, key);
			window.console.log('form', form);
			window.console.log('to', to);
			const nodes = graph.getNodes();
			const edges = graph.getEdges();
			edges.forEach((edge) => {
				const module = edge.getModel();
				window.console.log('module', module);
				if (
					module.source === form.nodeId &&
					module.sourceKey === form.key &&
					module.target === to.nodeId &&
					module.targetKey === to.key
				) {
					graph.update(edge, {
						isSelected: true,
					});
				}
			});
			nodes.forEach((node) => {
				const model = node.getModel();
				const selectedIndexList = model.selectedIndexList;
				// window.console.log('node', node);
				// window.console.log('model', model);
				let flag = false;
				model.attrs.forEach((attr, index) => {
					if (attr?.relation) {
						// window.console.log('attr', attr);
						// 有连接线，看连接线的id和key是否相同
						const cludes = attr.relation.find(
							(item) =>
								item.key === form.key &&
								item.nodeId === form.nodeId
						);
						if (cludes) {
							// window.console.log('node', node);
							// window.console.log('index', index);
							flag = true;
							selectedIndexList.push(index);
						}
					}
				});

				graph.update(node, {
					selectedIndexList: selectedIndexList,
				});
				if (flag) {
					selectedIndexList.forEach((select) => {
						this.previousFieldHighlight(
							graph,
							{
								nodeId: model.id,
								key: model.attrs[select].key,
							},
							form
						);
					});
				}
			});
		},
		nextFieldHighlight(graph, form, to) {
			// window.console.log('graph, nodeId, key', graph, nodeId, key);
			const nodes = graph.getNodes();
			const edges = graph.getEdges();
			// window.console.log('nodes', nodes);
			const node = nodes.find((item) => item.getModel().id === to.nodeId);
			// window.console.log('node,node', node);
			const model = node.getModel();
			const selectedIndexList = model.selectedIndexList;
			const selectedIndex = model.attrs.findIndex(
				(item) => item.key === to.key
			);
			selectedIndexList.push(selectedIndex);

			{
			}
			edges.forEach((edge) => {
				const module = edge.getModel();
				// window.console.log('module', module);
				if (
					module.source === form.nodeId &&
					module.sourceKey === form.key &&
					module.target === to.nodeId &&
					module.targetKey === to.key
				) {
					graph.update(edge, {
						isSelected: true,
					});
				}
			});
			graph.update(node, {
				selectedIndexList: selectedIndexList,
			});
			// window.console.log('edges', edges);
			// edges.forEach(edge=>{
			//   window.console.log('edge',edge);
			//   const model = edge.getModel()
			//   // const cfg = edge.getCfg()
			//   // window.console.log('cfg',cfg);
			//   window.console.log('model',model);

			//   model.stroke = 'red'
			//   model.style.stroke = 'red'
			//   // window.console.log('edge,edge.controlPoints',edge,edge.controlPoints);
			//   // edge.style.stroke = 'red';
			//   // edge.style.endArrow.fill = 'red';

			// })
			if (model.attrs[selectedIndex]?.relation) {
				// 如果有线
				model.attrs[selectedIndex].relation.forEach((item) => {
					this.nextFieldHighlight(graph, to, {
						nodeId: item.nodeId,
						key: item.key,
					});
				});
			}
			// window.console.log('node', node);
			// window.console.log('selectedIndexList', selectedIndexList);
		},
		clickNode(graph, e) {
			return;
			const nodes = graph.getNodes();
			window.console.log('nodes', nodes);
			window.console.log('graph', graph);
			window.console.log('e', e);
			const name = e.shape.get('name');
			const item = e.item;
			if (name && name.startsWith('item')) {
				// // window.console.log('移入设置样式');
				graph.updateItem(item, {
					selectedIndex: Number(name.split('-')[1]),
				});
			} else {
				// // window.console.log('移出设置样式');
				graph.updateItem(item, {
					selectedIndex: NaN,
				});
			}
		},
		G6fun() {},
		dataTransform(data) {
			const nodes = []; // 表字段信息
			const edges = []; // 连线信息

			data.map((node) => {
				nodes.push({
					...node,
				});
				if (node.attrs) {
					node.attrs.forEach((attr) => {
						if (attr.relation) {
							attr.relation.forEach((relation) => {
								edges.push({
									source: node.id,
									target: relation.nodeId,
									sourceKey: attr.key,
									targetKey: relation.key,
									label: relation.label,
								});
							});
						}
					});
				}
			});
			const obj = {
				nodes,
				edges,
			};
			// window.console.log('obj', obj);
			return {
				nodes,
				edges,
			};
		},
		initG6() {
			const _this = this;
			registerBehavior('dice-er-scroll', {
				getDefaultCfg() {
					return {
						multiple: true,
					};
				},
				getEvents() {
					return {
						itemHeight: 50,
						wheel: 'scorll',
						click: 'click',
						'node:mousemove': 'move',
					};
				},
				scorll(e) {
					e.preventDefault();
					const { graph } = this;
					const nodes = graph.getNodes().filter((n) => {
						const bbox = n.getBBox();

						return isInBBox(
							graph.getPointByClient(e.clientX, e.clientY),
							bbox
						);
					});

					if (nodes) {
						nodes.forEach((node) => {
							const model = node.getModel();
							if (model.attrs.length < 9) {
								return;
							}
							// window.console.log('e', e);
							// window.console.log('在box中滚动');
							const idx = model.startIndex || 0;
							let startX = model.startX || 0.5;
							let startIndex = idx + e.deltaY * 0.02;
							startX -= e.deltaX;
							if (startIndex < 0) {
								startIndex = 0;
							}
							if (startX > 0) {
								startX = 0;
							}
							if (startIndex > model.attrs.length - 1) {
								startIndex = model.attrs.length - 1;
							}
							graph.update(node, {
								startIndex,
								startX,
							});
						});
					}
				},
				click: throttle(function (e) {
					e.preventDefault();
					const { graph } = this;
					const nodes = graph.getNodes();
					// window.console.log('nodes',nodes);

					// window.console.log('nodes',nodes);
					// window.console.log('e', e);
					// const { graph } = this;
					// _this.clickNode(graph, e);
					const { y } = e;
					const item = e.item;
					const shape = e.shape;
					const name = e.shape?.get('name');

					window.console.log('item', item);
					window.console.log('shape', shape);
					window.console.log('name', name);

					if (!item) {
						return;
					}
					const edges = graph.getEdges();
					const model = item.getModel();
					// const getContainer = e.item.getContainer();
					// window.console.log('getContainer', getContainer);
					// window.console.log('item', item);
					// window.console.log('model', model);
					// window.console.log('model.edges', model.edges);
					// window.console.log('edges', edges);
					// const group = graph.get('group');
					// const path = graph.get('path');
					// window.console.log('group', group);
					// window.console.log('path', path);

					// 线
					{
					}
					// nodes.forEach((node) => {
					// 	window.console.log('node', node);
					// 	const model = node.getModel();
					// 	window.console.log('model', model);
					// 	graph.update(node, {
					// 		selectedIndex: 0,
					// 	});
					// });

					// 点击展开隐藏
					if (shape.get('name') === 'collapse') {
						// window.console.log('1111111--collapse-收起');
						graph.updateItem(item, {
							collapsed: true,
							size: [300, 50],
						});
						setTimeout(() => graph.layout(), 100);
					} else if (shape.get('name') === 'expand') {
						// window.console.log('2222--expand-展开');
						graph.updateItem(item, {
							collapsed: false,
							size: [300, 500],
						});
						setTimeout(() => graph.layout(), 100);
					} else {
						// 表字段
						// 所有取消高亮
						nodes.forEach((node) => {
							window.console.log('node', node);
							const model = node.getModel();
							graph.update(node, {
								selectedIndexList: [],
							});
						});
						edges.forEach((edge) => {
							graph.update(edge, {
								isSelected: false,
							});
						});

						const selfSelectedIndex = Number(name?.split('-')[1]);
						// 当前点击的字段高亮
						this.graph.updateItem(item, {
							selectedIndexList: [selfSelectedIndex],
						});
						// 之后的高亮
						if (model?.attrs[selfSelectedIndex]?.relation) {
							// 如果有线
							model.attrs[selfSelectedIndex].relation.forEach(
								(item) => {
									_this.nextFieldHighlight(
										graph,
										{
											nodeId: model.id,
											key: model.attrs[selfSelectedIndex]
												.key,
										},
										{
											nodeId: item.nodeId,
											key: item.key,
										}
									);
								}
							);
						}

						// 之前的高亮
						_this.previousFieldHighlight(
							graph,
							{
								nodeId: model.id,
								key: model?.attrs[selfSelectedIndex]?.key,
							},
							{
								nodeId: model.id,
								key: model.attrs[selfSelectedIndex].key,
							}
						);
					}
				}),
				move(e) {
					return;
					const { graph } = this;
					// _this.clickNode(graph, e);
					window.console.log('graph', graph);
					window.console.log('e', e);
					const name = e.shape.get('name');
					const item = e.item;
					window.console.log('item', item);
					window.console.log(
						'item.selectedIndex',
						item.selectedIndex
					);

					if (name && name.startsWith('item')) {
						// // window.console.log('移入设置样式');
						this.graph.updateItem(item, {
							selectedIndex: Number(name.split('-')[1]),
						});
					} else {
						// // window.console.log('移出设置样式');
						this.graph.updateItem(item, {
							selectedIndex: NaN,
						});
					}
				},
			});

			registerEdge('dice-er-edge', {
				/**
				 * 绘制节点，包含文本
				 * @param  {Object} cfg 节点的配置项
				 * @param  {G.Group} group 图形分组，节点中的图形对象的容器
				 * @return {G.Shape} 绘制的图形，通过 node.get('keyShape') 可以获取到
				 */
				draw(cfg, group) {
					// window.console.log('cfg ', cfg);
					// window.console.log('group', group);
					const edge = group.cfg.item;
					const sourceNode = edge.getSource().getModel();
					const targetNode = edge.getTarget().getModel();

					let sourceY = 15;
					let targetY = 15;
					const isSelected = cfg.isSelected;
					{
						const sourceIndex = sourceNode.attrs.findIndex(
							(e) => e.key === cfg.sourceKey
						);

						const sourceStartIndex = sourceNode.startIndex || 0;
						if (
							!sourceNode.collapsed &&
							sourceIndex > sourceStartIndex - 1
						) {
							sourceY =
								30 +
								(sourceIndex - sourceStartIndex + 0.5) * 30;
							sourceY = Math.min(sourceY, 300);
						}

						const targetIndex = targetNode.attrs.findIndex(
							(e) => e.key === cfg.targetKey
						);
						const targetStartIndex = targetNode.startIndex || 0;

						if (
							!targetNode.collapsed &&
							targetIndex > targetStartIndex - 1
						) {
							targetY =
								(targetIndex - targetStartIndex + 0.5) * 30 +
								30;
							targetY = Math.min(targetY, 300);
						}
					}
					//  sourceY = 15;
					//  targetY = 15;
					// // window.console.log('sourceY',sourceY);
					// // window.console.log('targetY',targetY);

					const startPoint = {
						...cfg.startPoint,
					};
					const endPoint = {
						...cfg.endPoint,
					};

					startPoint.y = startPoint.y + sourceY;
					endPoint.y = endPoint.y + targetY;

					let shape;

					if (sourceNode.id !== targetNode.id) {
						// window.console.log('11112222222222222222');
						// window.console.log('cfg ', cfg);
						// 连线
						// // window.console.log('sourceNode.id',sourceNode.id);
						// // window.console.log('targetNode.id',targetNode.id);
						shape = group.addShape('path', {
							attrs: {
								// stroke: '#5B8FF9',
								// stroke: 'green',
								// stroke: 'blue',
								stroke: isSelected ? 'green' : 'black',
								path: [
									['M', startPoint.x, startPoint.y],
									[
										'C',
										endPoint.x / 3 + (2 / 3) * startPoint.x,
										startPoint.y,
										endPoint.x / 3 + (2 / 3) * startPoint.x,
										endPoint.y,
										endPoint.x,
										endPoint.y,
									],
								],
								endArrow: true,
							},
							name: 'path-shape',
						});
					} else if (!sourceNode.collapsed) {
						// window.console.log('sourceNode.collapsed-11111',sourceNode.collapsed);
						let gap = Math.abs((startPoint.y - endPoint.y) / 3);
						if (startPoint['index'] === 1) {
							gap = -gap;
						}
						shape = group.addShape('path', {
							attrs: {
								stroke: '#5B8FF9',
								path: [
									['M', startPoint.x, startPoint.y],
									[
										'C',
										startPoint.x - gap,
										startPoint.y,
										startPoint.x - gap,
										endPoint.y,
										startPoint.x,
										endPoint.y,
									],
								],
								endArrow: true,
							},
							name: 'path-shape',
						});
					}
					// window.console.log('shape',shape);

					return shape;
				},
				/**
				 * 绘制后的附加操作，默认没有任何操作
				 * @param  {Object} cfg 节点的配置项
				 * @param  {G.Group} group 图形分组，节点中的图形对象的容器
				 */
				afterDraw(cfg, group) {
					// window.console.log('cfg ', cfg);
					// window.console.log('group', group);
					const labelCfg = cfg.labelCfg || {};
					const edge = group.cfg.item;
					const sourceNode = edge.getSource().getModel();
					const targetNode = edge.getTarget().getModel();
					if (sourceNode.collapsed && targetNode.collapsed) {
						return;
					}
					const path = group.find(
						(element) => element.get('name') === 'path-shape'
					);

					const labelStyle = Util.getLabelPosition(
						path,
						0.5,
						0,
						0,
						true
					);
					// 连线上文本的颜色
					const label = group.addShape('text', {
						attrs: {
							...labelStyle,
							text: cfg.label || '',
							fill: '#000',
							textAlign: 'center',
							stroke: '#fff',
							// stroke: 'blue',
							lineWidth: 1,
						},
					});
					label.rotateAtStart(labelStyle.rotate);
				},
			});
			registerNode('dice-er-box', {
				draw(cfg, group) {
					// window.console.log('cfg ', cfg);
					// window.console.log('group', group);
					// const labelCfg = cfg.labelCfg || {};
					// const edge = group.cfg.item;
					// const sourceNode = edge.getSource().getModel();
					// const targetNode = edge.getTarget().getModel();
					const width = 250;
					const height = 316;
					// const height = cfg.attrs.length * 30 + 30;
					const itemCount = 10;
					// 表边框
					const boxStyle = {
						stroke: '#096DD9',
						// stroke: '#ccc',
						radius: 4,
					};
					// window.console.log('group',group);

					const {
						attrs = [],
						startIndex = 0,
						selectedIndexList = [],
						collapsed,
						icon,
					} = cfg;
					const list = attrs;
					const afterList = list.slice(
						Math.floor(startIndex),
						Math.floor(startIndex + itemCount - 1)
					);
					const offsetY = (0.5 - (startIndex % 1)) * itemHeight + 30;
					// 表级头部背景
					group.addShape('rect', {
						attrs: {
							fill: boxStyle.stroke,
							height: 30,
							width,
							radius: [boxStyle.radius, boxStyle.radius, 0, 0],
						},
						draggable: true,
					});

					let fontLeft = 12;

					if (icon && icon.show !== false) {
						group.addShape('image', {
							attrs: {
								x: 8,
								y: 8,
								height: 16,
								width: 16,
								...icon,
							},
						});
						fontLeft += 18;
					}
					// 表级头部文本
					group.addShape('text', {
						attrs: {
							y: 22,
							x: fontLeft,
							fill: '#fff',
							text: cfg.label,
							fontSize: 12,
							fontWeight: 500,
						},
					});
					// 表级底部颜色
					group.addShape('rect', {
						attrs: {
							x: 0,
							y: collapsed ? 30 : 300,
							height: 15,
							width,
							fill: '#eee',
							radius: [0, 0, boxStyle.radius, boxStyle.radius],
							cursor: 'pointer',
						},
						name: collapsed ? 'expand' : 'collapse', // 展开和收起
					});
					// 表级底部文本
					group.addShape('text', {
						attrs: {
							x: width / 2 - 6,
							y: (collapsed ? 30 : 300) + 12,
							text: collapsed ? '+' : '-',
							// text: collapsed ? '展开' : '收起',
							width,
							fill: '#000',
							radius: [0, 0, boxStyle.radius, boxStyle.radius],
							cursor: 'pointer',
						},
						name: collapsed ? 'expand' : 'collapse',
					});

					// 边框
					const keyshape = group.addShape('rect', {
						attrs: {
							x: 0,
							y: 0,
							width,
							height: collapsed ? 45 : height,
							// height:height,
							...boxStyle,
						},
						draggable: true,
					});

					if (collapsed) {
						return keyshape;
					}
					// 字段box
					const listContainer = group.addGroup({});
					listContainer.setClip({
						type: 'rect',
						attrs: {
							x: -8,
							y: 30,
							width: width + 16,
							height: 300 - 30,
						},
					});

					listContainer.addShape({
						type: 'rect',
						attrs: {
							x: 1,
							y: 30,
							width: width - 2,
							height: 300 - 30,
							// fill: '#fff',
							// fill: 'yellow',
						},
						draggable: true,
					});

					if (list.length > itemCount) {
						// 滚动条
						const barStyle = {
							width: 4,
							padding: 0,
							boxStyle: {
								stroke: '#00000022',
								// stroke: 'green',
							},
							innerStyle: {
								// fill: 'red',
								fill: '#00000022',
							},
						};

						listContainer.addShape('rect', {
							attrs: {
								y: 30,
								x: width - barStyle.padding - barStyle.width,
								width: barStyle.width,
								// height: height,
								height: height - 30,
								...barStyle.boxStyle,
							},
						});

						const indexHeight =
							afterList.length > itemCount
								? (afterList.length / list.length) * height
								: 10;

						listContainer.addShape('rect', {
							attrs: {
								y:
									30 +
									barStyle.padding +
									(startIndex / list.length) * (height - 30),
								x: width - barStyle.padding - barStyle.width,
								width: barStyle.width,
								height: Math.min(height, indexHeight),
								// height: height,
								// height: indexHeight,
								...barStyle.innerStyle,
							},
						});
					}
					if (afterList) {
						afterList.forEach((e, i) => {
							const isSelected = selectedIndexList.includes(
								Math.floor(startIndex) + i
							);
							let { key = '', type } = e;
							if (type) {
								key += ' - ' + type;
							}
							const label =
								key.length > 26
									? key.slice(0, 24) + '...'
									: key;

							listContainer.addShape('rect', {
								attrs: {
									x: 1,
									y:
										i * itemHeight -
										itemHeight / 2 +
										offsetY,
									width: width - 4,
									height: itemHeight,
									// height: height,
									radius: 2,
									lineWidth: 1,
									cursor: 'pointer',
									fill: isSelected ? 'red' : '',
								},
								name: `item-${
									Math.floor(startIndex) + i
								}-content`,
								draggable: true,
							});

							if (!cfg.hideDot) {
								//左边连接圆点
								listContainer.addShape('circle', {
									attrs: {
										x: 0,
										y: i * itemHeight + offsetY,
										r: 3,
										stroke: boxStyle.stroke,
										fill: 'white',
										radius: 2,
										lineWidth: 1,
										cursor: 'pointer',
									},
								});
								//右边连接圆点
								listContainer.addShape('circle', {
									attrs: {
										x: width,
										y: i * itemHeight + offsetY,
										r: 3,
										stroke: boxStyle.stroke,
										fill: 'white',
										radius: 2,
										lineWidth: 1,
										cursor: 'pointer',
									},
								});
							}
							// 字段
							listContainer.addShape('text', {
								attrs: {
									x: 12,
									y: i * itemHeight + offsetY + 6,
									text: label,
									fontSize: 12,
									fill: '#000',
									fontFamily:
										'Avenir,-apple-system,BlinkMacSystemFont,Segoe UI,PingFang SC,Hiragino Sans GB,Microsoft YaHei,Helvetica Neue,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol',
									full: e,
									fontWeight: isSelected ? 500 : 100,
									cursor: 'pointer',
								},
								name: `item-${Math.floor(startIndex) + i}`,
							});
						});
					}

					return keyshape;
				},
				/**
				 * 获取锚点（相关边的连入点）
				 * @param  {Object} cfg 节点的配置项
				 * @return {Array|null} 锚点（相关边的连入点）的数组,如果为 null，则没有锚点
				 */
				getAnchorPoints() {
					return [
						[0, 0],
						[1, 0],
					];
				},
			});

			const container = document.getElementById('container');

			const width = container.scrollWidth;
			const height = container.scrollHeight;
			const graph = new G6.Graph({
				container: 'container',
				width,
				height,
				// 默认状态下节点的配置
				defaultNode: {
					size: [400, 400],
					type: 'dice-er-box',
					color: '#5B8FF9',
					style: {
						fill: '#9EC9FF',
						lineWidth: 3,
					},
					labelCfg: {
						style: {
							fill: 'black',
							fontSize: 20,
						},
					},
				},
				// 默认状态下边的配置
				defaultEdge: {
					type: 'dice-er-edge',
					style: {
						stroke: '#e2e2e2',
						lineWidth: 4,
						endArrow: true,
					},
				},
				// 设置画布的交互模式
				modes: {
					default: ['dice-er-scroll', 'drag-node', 'drag-canvas'],
				},
				// 布局配置项
				layout: {
					type: 'dagre',
					rankdir: 'LR',
					align: 'UL',
					controlPoints: true,
					nodesepFunc: () => 0.2,
					ranksepFunc: () => 0.5,
				},
				animate: true,
			});

			graph.data(this.dataTransform(rawData));

			graph.render();
			graph.on('edge:mouseenter', (e) => {
				graph.setItemState(e.item, 'active', true);
			});
			graph.on('edge:mouseleave', (e) => {
				graph.setItemState(e.item, 'active', false);
			});
			// graph.on('node:mouseenter', (evt) => {
			// 	const { item } = evt;
			// 	graph.setItemState(item, 'active', true);
			// });

			// graph.on('node:mouseleave', (evt) => {
			// 	const { item } = evt;
			// 	graph.setItemState(item, 'active', false);
			// });

			// graph.on('node:click', (evt) => {
			// 	const { item } = evt;
			// 	graph.setItemState(item, 'selected', true);
			// });
			// graph.on('canvas:click', (evt) => {
			// 	graph.getNodes().forEach((node) => {
			// 		graph.clearItemStates(node);
			// 	});
			// });
		},
	},
};
</script>
<style lang="scss" scoped></style>
